/*
Miranda IM for Windows Search

Copyright (C) 2012-2014 Nikolay Raspopov

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "stdafx.h"
#include "MirandaSearch.h"
#include "MirandaProtocol.h"
#include "MirandaAccessor.h"
#include "MirandaSearch.h"
#include "MirandaPlugin.h"

struct
{
	HRESULT	hr;
	DWORD	err;
}
const static INET_CODES[] =
{
	{ S_OK,							200 },
	{ INET_E_INVALID_URL,			400 },
	{ INET_E_OBJECT_NOT_FOUND,		404 },
	{ INET_E_CONNECTION_TIMEOUT,	503 }
};

static HRESULT Report( HRESULT hr, IInternetProtocolSink* pOIProtSink )
{
	for ( int i = 0; i < _countof( INET_CODES ); ++i )
	{
		if ( INET_CODES[ i ].hr == hr )
			return pOIProtSink->ReportResult( INET_CODES[ i ].hr, INET_CODES[ i ].err, NULL );
	}
	ATLASSERT( FALSE );
	return E_UNEXPECTED;
}

struct
{
	UINT	id;
	LPCTSTR	suffix;
}
const static INET_TEMPLATES[] =
{
	{ IDR_MESSAGE,	_T( "message.template" ) },
	{ IDR_ERROR,	_T( "error.template" ) }
};

static LPCTSTR Suffix( UINT nID )
{
	for ( int i = 0; i < _countof( INET_TEMPLATES ); ++i )
	{
		if ( INET_TEMPLATES[ i ].id == nID )
			return INET_TEMPLATES[ i ].suffix;
	}
	ATLASSERT( FALSE );
	return NULL;
}

void LoadTemplate(UINT nID, CStringA& sData)
{
	sData.Empty();

	// Load template from file
	{
		const int nPos = _AtlModule.m_sModule.ReverseFind( _T( '\\' ) );
		const CString sTemplate = _AtlModule.m_sModule.Left( nPos + 1 ) + DATA_DIR + _AtlModule.m_sModule.Mid( nPos, _AtlModule.m_sModule.GetLength() - nPos - 6 ) + Suffix( nID );
		CAtlFile oTemplate;
		if ( oTemplate.Create( sTemplate, FILE_GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING ) == S_OK )
		{
			CAtlFileMapping< char > oTemplateMap;
			if ( oTemplateMap.MapFile( oTemplate ) == S_OK )
			{
				sData.Append( (LPCSTR)oTemplateMap, (int)oTemplateMap.GetMappingSize() );
				oTemplateMap.Unmap();
			}
		}
		else
			ATLTRACE( "LoadTemplate() : Failed to open file: \"%s\"\n", (LPCSTR)CT2A( sTemplate ) );
	}

	// Check if this is a valid template
	if ( sData.IsEmpty() )
	{
		// Use default template instead
		if ( HRSRC hResource = ::FindResource( _AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE( nID ), _T( "TEMPLATE" ) ) )
		{
			if ( HGLOBAL hGlobal = LoadResource( _AtlBaseModule.GetResourceInstance(), hResource ) )
			{
				if ( LPVOID pImage = LockResource( hGlobal ) )
				{
					sData.Append( (LPCSTR)pImage, (int)SizeofResource( _AtlBaseModule.GetResourceInstance(), hResource ) );
				}
			}
		}
	}

	ATLASSERT( ! sData.IsEmpty() );
}

// CMirandaProtocol

CMirandaProtocol::CMirandaProtocol()
{
}

HRESULT CMirandaProtocol::FinalConstruct()
{
	return CoCreateFreeThreadedMarshaler( GetControllingUnknown(), &m_pUnkMarshaler.p );
}

void CMirandaProtocol::FinalRelease()
{
	m_pUnkMarshaler.Release();
}

// ISearchProtocol

STDMETHODIMP CMirandaProtocol::Init(TIMEOUT_INFO* /*pTimeoutInfo*/, IProtocolHandlerSite* /*pProtocolHandlerSite*/, PROXY_INFO* /*pProxyInfo*/)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocol::Init() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

STDMETHODIMP CMirandaProtocol::CreateAccessor(PCWSTR pcwszURL, AUTHENTICATION_INFO *pAuthenticationInfo, INCREMENTAL_ACCESS_INFO *pIncrementalAccessInfo, ITEM_INFO *pItemInfo, __deref_out IUrlAccessor **ppAccessor)
{
	return CreateAccessorEx( pcwszURL, pAuthenticationInfo, pIncrementalAccessInfo, pItemInfo, NULL, ppAccessor );
}

STDMETHODIMP CMirandaProtocol::CloseAccessor(IUrlAccessor* /*pAccessor*/)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocol::CloseAccessor() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

STDMETHODIMP CMirandaProtocol::ShutDown(void)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocol::ShutDown() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

// ISearchProtocol2

STDMETHODIMP CMirandaProtocol::CreateAccessorEx(PCWSTR pcwszURL, AUTHENTICATION_INFO * /*pAuthenticationInfo*/, INCREMENTAL_ACCESS_INFO * /*pIncrementalAccessInfo*/, ITEM_INFO * /*pItemInfo*/, const BLOB * /*pUserData*/, __deref_out IUrlAccessor **ppAccessor)
{
	if ( ! ppAccessor )
		return E_POINTER;

	*ppAccessor = NULL;

	CComObject< CMirandaAccessor >* pUrlAccessor;
	HRESULT hr = CComObject< CMirandaAccessor >::CreateInstance( &pUrlAccessor );
	if ( SUCCEEDED( hr ) )
	{
		hr = pUrlAccessor->Init( pcwszURL );
		if ( SUCCEEDED( hr ) )
		{
			hr = pUrlAccessor->QueryInterface( ppAccessor );
		}
		if ( FAILED( hr ) )
			pUrlAccessor->Release();
	}

#ifdef _ATL_DEBUG_QI
	if ( FAILED( hr ) )
		ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocol2::CreateAccessorEx( %s ) : 0x%08x\n", GetCurrentThreadId(), (LPCSTR)CW2A( pcwszURL ), hr );
	else
		ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocol2::CreateAccessorEx( %s ) -> S_OK\n", GetCurrentThreadId(), (LPCSTR)CW2A( pcwszURL ) );
#endif

	return hr;
}

// ISearchProtocolThreadContext

STDMETHODIMP CMirandaProtocol::ThreadInit(void)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocolThreadContext::ThreadInit() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

STDMETHODIMP CMirandaProtocol::ThreadShutdown(void)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocolThreadContext::ThreadShutdown() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

STDMETHODIMP CMirandaProtocol::ThreadIdle(DWORD /*dwTimeElaspedSinceLastCallInMS*/)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : ISearchProtocolThreadContext::ThreadIdle() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

// IInternetProtocol

STDMETHODIMP CMirandaProtocol::Abort(HRESULT /*hrReason*/, DWORD /*dwOptions*/)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : IInternetProtocol::Abort() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

STDMETHODIMP CMirandaProtocol::Continue(PROTOCOLDATA* /*pProtocolData*/)
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : IInternetProtocol::Continue() -> S_OK\n", GetCurrentThreadId() );
#endif
	return S_OK;
}

STDMETHODIMP CMirandaProtocol::Resume()
{
#ifdef _ATL_DEBUG_QI
	ATLTRACE( "0x%08x : CMirandaProtocol : IInternetProtocol::Resume() -> E_NOTIMPL\n", GetCurrentThreadId() );
#endif
	return E_NOTIMPL;
}

STDMETHODIMP CMirandaProtocol::Start(LPCWSTR szUrl, IInternetProtocolSink* pOIProtSink, IInternetBindInfo* /*pOIBindInfo*/, DWORD grfPI, HANDLE_PTR /*dwReserved*/)
{
	HRESULT hr = INET_E_INVALID_URL;

	m_sData.Empty();

	CString sRelativeURL;
	const URLLevel nLevel = ParseURL( szUrl, sRelativeURL );
	if ( nLevel == URL_LEVEL_BAD )
	{
		if ( ( grfPI & PI_PARSE_URL ) != 0 )
		{
			ATLTRACE( "IInternetProtocol : Start( \"%s\", 0x%08x ) -> S_FALSE (error)\n", (LPCSTR)CW2A( szUrl ), grfPI );
			hr = S_FALSE;
		}
		else
			ATLTRACE( "IInternetProtocol : Start( \"%s\", 0x%08x ) -> INET_E_INVALID_URL (error)\n", (LPCSTR)CW2A( szUrl ), grfPI );
	}
	else if ( ( grfPI & PI_PARSE_URL ) != 0 )
	{
		ATLTRACE( "IInternetProtocol : Start( \"%s\", 0x%08x ) -> S_OK (parse only)\n", (LPCSTR)CW2A( szUrl ), grfPI );
		hr = S_OK;
	}
	else
	{
		switch ( nLevel )
		{
		case URL_LEVEL_ROOT:
			hr = ReadRoot();
			break;

		case URL_LEVEL_CONTACTS:
			hr = ReadContacts();
			break;

		case URL_LEVEL_EVENTS:
			hr = ReadEvents( sRelativeURL );
			break;

		case URL_LEVEL_MESSAGE:
			hr = ReadMessage( sRelativeURL );
			break;

		default:
			ATLASSERT( FALSE );
		}
	}

	if ( hr != S_OK )
	{
		LoadTemplate( IDR_ERROR, m_sData );
	}

	if ( pOIProtSink && m_sData.GetLength() )
	{
		pOIProtSink->ReportProgress( BINDSTATUS_MIMETYPEAVAILABLE, CComBSTR( CFSTR_MIME_HTML ) );
		ATLTRACE( "IInternetProtocol : Start : ReportProgress() -> BINDSTATUS_MIMETYPEAVAILABLE\n" );

		pOIProtSink->ReportProgress( BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE, CComBSTR( CFSTR_MIME_HTML ) );
		ATLTRACE( "IInternetProtocol : Start : ReportProgress() -> BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE\n" );

		pOIProtSink->ReportData( BSCF_LASTDATANOTIFICATION, m_sData.GetLength(), m_sData.GetLength() );
		ATLTRACE( "IInternetProtocol : Start : ReportData() -> BSCF_LASTDATANOTIFICATION\n" );

		Report( hr, pOIProtSink );
		ATLTRACE( "IInternetProtocol : Start : ReportResult() -> 0x%08x\n", hr );
	}

	return hr;
}

HRESULT CMirandaProtocol::ReadRoot()
{
	if ( ! _AtlModule.Open() )
	{
		ATLTRACE( "IInternetProtocol : Start : ReadRoot() -> INET_E_CONNECTION_TIMEOUT\n" );
		return INET_E_CONNECTION_TIMEOUT;
	}

	CStringA sTitle = UTF8Encode( TITLE );

	CStringA sNav = "<div class=\"nav\">&nbsp;</div>\n";

	CStringA sTable = "<table>\n";
	sTable.AppendFormat( "<tr><td width=\"100%%\" class=\"messages_odd\"><a href=\"%s\">%s</a></td></tr>\n",
		UTF8Encode( GetCurrentUserURL() ), UTF8Encode( _AtlModule.GetMe() ) );
	sTable += "</table>\n";

	// Replace fields
	LoadTemplate( IDR_MESSAGE, m_sData );
	m_sData.Replace( "<% TITLE %>", sTitle );
	m_sData.Replace( "<% NAV %>", sNav );
	m_sData.Replace( "<% MESSAGES %>", sTable );

	ATLTRACE( "IInternetProtocol : Start : ReadRoot() -> S_OK (%d bytes)\n", m_sData.GetLength() );
	return S_OK;
}

HRESULT CMirandaProtocol::ReadContacts()
{
	CAtlArray< BYTE > pData;
	if ( ! _AtlModule.ReadData( _T("/"), pData ) )
	{
		ATLTRACE( "IInternetProtocol : Start : ReadContacts() -> INET_E_CONNECTION_TIMEOUT\n" );
		return INET_E_CONNECTION_TIMEOUT;
	}

	CStringA sTitle = UTF8Encode( TITLE _T( " - " ) + _AtlModule.GetMe() );

	CStringA sNav = "<div class=\"nav\">";
	sNav.AppendFormat( "<a href=\"%s/\">%s</a>", UTF8Encode( PROTOCOL ), UTF8Encode( TITLE ) );
	sNav += "</div>\n";

	CStringA sTable = "<table>\n";
	size_t nSize = pData.GetCount();
	const BYTE* pBuf = (const BYTE*)pData.GetData();
	int row = 0;
	while ( nSize > sizeof( ListData ) + sizeof( WCHAR ) )
	{
		const ListData& data = *(const ListData*)pBuf;

		sTable.AppendFormat( "<tr><td width=\"100%%\" class=\"%s\"><a href=\"%s%s/\">%s</a></td></tr>\n",
			( ( ( row & 1 ) == 0 ) ? "messages_odd" : "messages_even" ),
			UTF8Encode( GetCurrentUserURL() ), UTF8Encode( URLEncode( data.wszName ) ), UTF8Encode( Escape( data.wszName ) ) );

		const size_t nLen = sizeof( ListData ) + ( wcslen( data.wszName ) + 1 ) * sizeof( WCHAR );
		pBuf += nLen;
		nSize -= nLen;
		++row;
	}
	if ( ! row )
		sTable += "<tr><td width=\"100%%\" class=\"%s\">No contacts</td></tr>\n";
	sTable += "</table>\n";

	// Replace fields
	LoadTemplate( IDR_MESSAGE, m_sData );
	m_sData.Replace( "<% TITLE %>", sTitle );
	m_sData.Replace( "<% NAV %>", sNav );
	m_sData.Replace( "<% MESSAGES %>", sTable );

	ATLTRACE( "IInternetProtocol : Start : ReadConacts() -> S_OK (%d bytes)\n", m_sData.GetLength() );
	return S_OK;
}

HRESULT CMirandaProtocol::ReadEvents( const CString& sRelativeURL)
{
	const CString sName = URLDecode( sRelativeURL.Mid( 1, sRelativeURL.GetLength() - 2 ) );

	CAtlArray< BYTE > pData;
	if ( ! _AtlModule.ReadData( sRelativeURL, pData ) )
	{
		ATLTRACE( "IInternetProtocol : Start : ReadEvents( \"%s\" ) -> INET_E_CONNECTION_TIMEOUT\n", (LPCSTR)CW2A( sRelativeURL ) );
		return INET_E_CONNECTION_TIMEOUT;
	}
	if ( pData.GetCount() && pData.GetCount() % sizeof( time_t ) != 0 )
	{
		ATLTRACE( "IInternetProtocol : Start : ReadEvents( \"%s\" ) -> INET_E_INVALID_URL\n", (LPCSTR)CW2A( sRelativeURL ) );
		return INET_E_INVALID_URL;
	}
	const int nCount = (int)( pData.GetCount() / sizeof( time_t ) );

	CStringA sTitle = UTF8Encode( TITLE _T( " - " ) + _AtlModule.GetMe() + _T( " - " ) + Escape( sName ) );

	CStringA sNav = "<div class=\"nav\">";
	sNav.AppendFormat( "<a href=\"%s/\">%s</a>", UTF8Encode( PROTOCOL ), UTF8Encode( TITLE ) );
	sNav.AppendFormat( " | <a href=\"%s\">%s</a>", UTF8Encode( GetCurrentUserURL() ), UTF8Encode( _AtlModule.GetMe() ) );
	sNav += "</div>\n";

	CStringA sTable = "<table>\n";
	int row = 0;
	for ( int i = 0; i < nCount; i += 10 )
	{
		sTable.AppendFormat( "<tr><td width=\"100%%\" class=\"%s\"><a href=\"%s%s/%d\">%d ...</a></td></tr>\n",
			( ( ( row & 1 ) == 0 ) ? "messages_odd" : "messages_even" ),
			UTF8Encode( GetCurrentUserURL() ), UTF8Encode( URLEncode( sName ) ), i + 1, i + 1 );
		++row;
	}
	if ( ! row )
		sTable += "<tr><td width=\"100%%\" class=\"%s\">No messages</td></tr>\n";
	sTable += "</table>\n";


	// Replace fields
	LoadTemplate( IDR_MESSAGE, m_sData );
	m_sData.Replace( "<% TITLE %>", sTitle );
	m_sData.Replace( "<% NAV %>", sNav );
	m_sData.Replace( "<% MESSAGES %>", sTable );

	ATLTRACE( "IInternetProtocol : Start : ReadEvents( \"%s\" ) -> S_OK (%d bytes)\n", (LPCSTR)CW2A( sRelativeURL ), m_sData.GetLength() );
	return S_OK;
}

HRESULT CMirandaProtocol::ReadMessage(const CString& sRelativeURL)
{
	const int nSlash = sRelativeURL.Find( _T('/'), 1 );
	const CString sName = URLDecode( sRelativeURL.Mid( 1, nSlash - 1 ) );

	const int nBaseMessageID = _tstoi( sRelativeURL.Mid( nSlash + 1 ) );
	if ( nBaseMessageID < 1 )
	{
		ATLTRACE( "IInternetProtocol : Start : ReadMessage( \"%s\" ) -> INET_E_INVALID_URL\n", (LPCSTR)CW2A( sRelativeURL ) );
		return INET_E_INVALID_URL;
	}

	const int nStep = 10;
	const int nStartID = max( 1, nBaseMessageID - nStep );
	int nEndID = 1;
	int row = 0;
	CStringA sTable = "<table>\n";
	for ( int nMessageID = nStartID; nMessageID <= nBaseMessageID + nStep; ++nMessageID )
	{
		CString sMessageURL = sRelativeURL.Left( nSlash + 1 );
		sMessageURL.AppendFormat( _T("%d"), nMessageID );

		CAtlArray< BYTE > pData;
		if ( ! _AtlModule.ReadData( sMessageURL, pData ) )
		{
			if ( nBaseMessageID >= nMessageID )
			{	
				ATLTRACE( "IInternetProtocol : Start : ReadMessage( \"%s\" ) -> INET_E_CONNECTION_TIMEOUT (\"%s\")\n", (LPCSTR)CW2A( sRelativeURL ), (LPCSTR)CW2A( sMessageURL ) );
				return INET_E_CONNECTION_TIMEOUT;
			}
			break;
		}

		const EventData& data = *(const EventData*)pData.GetData();
		const size_t nTextLength = pData.GetCount() - sizeof( EventData );
		if ( pData.GetCount() <= sizeof( EventData ) || ( nTextLength & 1 ) != 0 || ! data.tTimestamp )
		{
			if ( nBaseMessageID >= nMessageID )
			{
				ATLTRACE( "IInternetProtocol : Start : ReadMessage( \"%s\" ) -> INET_E_OBJECT_NOT_FOUND (\"%s\")\n", (LPCSTR)CW2A( sRelativeURL ), (LPCSTR)CW2A( sMessageURL ) );
				return INET_E_OBJECT_NOT_FOUND;
			}
			break;
		}

		nEndID = nMessageID;
		if ( nMessageID == nBaseMessageID + nStep )
			break;

		const ULONGLONG nTimestamp = ( (ULONGLONG)data.tTimestamp + 11644473600ui64 ) * 10000000ui64;
		const FILETIME ftTimestamp = { (DWORD)nTimestamp, (DWORD)( nTimestamp >> 32 ) };
		const CString sFrom = ( data.nFlags & DBEF_SENT ) ? _AtlModule.GetMe() : sName;
		CString sMessage( data.wszText, (int)nTextLength / sizeof( WCHAR ) );
		sMessage.Remove( _T( '\r' ) );
		sMessage = Escape( sMessage );
		sMessage.Replace( _T( "\n" ), _T( "<br/>" ) );

		sTable.AppendFormat( "<tr>\n\t<td nowrap class=\"%s\">%s</td>\n\t<td nowrap class=\"%s\">%s</td>\n\t<td width=\"100%%\" class=\"%s\">: %s</td>\n</tr>\n",
			( ( nBaseMessageID == nMessageID ) ? "messages_selected" : ( ( ( row & 1 ) == 0 ) ? "messages_odd" : "messages_even" ) ),
			UTF8Encode( COleDateTime( ftTimestamp ).Format( _T("[%c] ") ) ),
			( ( nBaseMessageID == nMessageID ) ? "messages_selected" : ( ( ( row & 1 ) == 0 ) ? "messages_odd" : "messages_even" ) ),
			UTF8Encode( sFrom ),
			( ( nBaseMessageID == nMessageID ) ? "messages_selected" : ( ( ( row & 1 ) == 0 ) ? "messages_odd" : "messages_even" ) ),
			UTF8Encode( sMessage ) );

		++row;
	}
	sTable += "</table>\n";

	CStringA sTitle = UTF8Encode( TITLE _T( " - " ) + _AtlModule.GetMe() + _T( " - " ) + Escape( sName ) );
	
	CStringA sNav = "<div class=\"nav\">";
	sNav.AppendFormat( "<a href=\"%s/\">%s</a>", UTF8Encode( PROTOCOL ), UTF8Encode( TITLE ) );
	sNav.AppendFormat( " | <a href=\"%s\">%s</a>", UTF8Encode( GetCurrentUserURL() ), UTF8Encode( _AtlModule.GetMe() ) );
	sNav.AppendFormat( " | <a href=\"%s%s/\">%s</a>", UTF8Encode( GetCurrentUserURL() ), UTF8Encode( URLEncode( sName ) ), UTF8Encode( sName ) );
	if ( nStartID > 1 )
		sNav.AppendFormat( " | <a href=\"%s%s/%d\">&lt;&lt;</a>", UTF8Encode( GetCurrentUserURL() ), UTF8Encode( URLEncode( sName ) ), nStartID );
	if ( nEndID == nBaseMessageID + nStep )
		sNav.AppendFormat( " | <a href=\"%s%s/%d\">&gt;&gt;</a>", UTF8Encode( GetCurrentUserURL() ), UTF8Encode( URLEncode( sName ) ), nEndID );
	sNav += "</div>\n";

	// Replace fields
	LoadTemplate( IDR_MESSAGE, m_sData );
	m_sData.Replace( "<% TITLE %>", sTitle );
	m_sData.Replace( "<% NAV %>", sNav );
	m_sData.Replace( "<% MESSAGES %>", sTable );

	ATLTRACE( "IInternetProtocol : Start : ReadMessage( \"%s\" ) -> S_OK (%d bytes)\n", (LPCSTR)CW2A( sRelativeURL ), m_sData.GetLength() );
	return S_OK;
}

STDMETHODIMP CMirandaProtocol::Suspend()
{
	ATLTRACE( "IInternetProtocol : Suspend() -> E_NOTIMPL\n" );

	return E_NOTIMPL;
}

STDMETHODIMP CMirandaProtocol::Terminate(DWORD /*dwOptions*/)
{
	ATLTRACE( "IInternetProtocol : Terminate() -> S_OK\n" );

	return S_OK;
}

STDMETHODIMP CMirandaProtocol::LockRequest(DWORD /*dwOptions*/)
{
	ATLTRACE( "IInternetProtocol : LockRequest() -> S_OK\n" );

	return S_OK;
}

STDMETHODIMP CMirandaProtocol::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
	const ULONG nRead = min( (ULONG)m_sData.GetLength(), cb );
	if ( nRead )
	{
		memcpy( pv, m_sData, nRead );
		m_sData = m_sData.Mid( nRead );
	}
	if ( pcbRead )
		*pcbRead = nRead;

	ATLTRACE( "IInternetProtocol : Read( buffer for %u bytes ) : Returned %u bytes -> %s\n", cb, nRead, ( m_sData.IsEmpty() ? "S_FALSE (complete)" : "S_OK (pending)" ) );
	return m_sData.IsEmpty() ? S_FALSE : S_OK;
}

STDMETHODIMP CMirandaProtocol::Seek(LARGE_INTEGER /*dlibMove*/, DWORD /*dwOrigin*/, ULARGE_INTEGER* /*plibNewPosition*/)
{
	ATLTRACE( "IInternetProtocol : Seek() -> E_FAIL (not supported)\n" );
	return E_FAIL;
}

STDMETHODIMP CMirandaProtocol::UnlockRequest()
{
	ATLTRACE( "IInternetProtocol : UnlockRequest() -> S_OK\n" );
	return S_OK;
}

// IInternetProtocolInfo

STDMETHODIMP CMirandaProtocol::ParseUrl(LPCWSTR pwzUrl, PARSEACTION ParseAction, DWORD /*dwParseFlags*/, LPWSTR pwzResult, DWORD cchResult, DWORD* pcchResult, DWORD /*dwReserved*/)
{
	pwzUrl;

	switch ( ParseAction )
	{
	/*case PARSE_CANONICALIZE:
		*pcchResult = (DWORD)wcslen( pwzUrl ) + 1;
		if ( cchResult < *pcchResult || pwzResult == NULL )
		{
			ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_CANONICALIZE ) -> S_FALSE\n", (LPCSTR)CW2A( pwzUrl ) );
			return S_FALSE;
		}
		wcscpy_s( pwzResult, cchResult, pwzUrl );
		ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_CANONICALIZE ) -> S_OK (\"%s\")\n", (LPCSTR)CW2A( pwzUrl ), (LPCSTR)CW2A( pwzResult ) );
		return S_OK;*/

	/*case PARSE_DOMAIN:
		*pcchResult = (DWORD)_countof( PROTOCOL );
		if ( cchResult < *pcchResult || pwzResult == NULL )
		{
			ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_DOMAIN ) -> S_FALSE\n", (LPCSTR)CW2A( pwzUrl ) );
			return S_FALSE;
		}
		wcscpy_s( pwzResult, cchResult, PROTOCOL );
		ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_DOMAIN ) -> S_OK (\"%s\")\n", (LPCSTR)CW2A( pwzUrl ), (LPCSTR)CW2A( pwzResult ) );
		return S_OK;*/

	case PARSE_SECURITY_URL:
		*pcchResult = (DWORD)_countof( ZONE );
		if ( cchResult < *pcchResult || pwzResult == NULL )
		{
			ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_SECURITY_URL ) -> S_FALSE\n", (LPCSTR)CW2A( pwzUrl ) );
			return S_FALSE;
		}
		wcscpy_s( pwzResult, cchResult, ZONE );
		ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_SECURITY_URL ) -> S_OK (\"%s\")\n", (LPCSTR)CW2A( pwzUrl ), (LPCSTR)CW2A( pwzResult ) );
		return S_OK;

	case PARSE_SECURITY_DOMAIN:
		*pcchResult = (DWORD)_countof( ZONE );
		if ( cchResult < *pcchResult || pwzResult == NULL )
		{
			ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_SECURITY_DOMAIN ) -> S_FALSE\n", (LPCSTR)CW2A( pwzUrl ) );
			return S_FALSE;
		}
		wcscpy_s( pwzResult, cchResult, ZONE );
		ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", PARSE_SECURITY_DOMAIN ) -> S_OK (\"%s\")\n", (LPCSTR)CW2A( pwzUrl ), (LPCSTR)CW2A( pwzResult ) );
		return S_OK;
	}

	ATLTRACE( "IInternetProtocolInfo : ParseUrl( \"%s\", %d ) -> INET_E_DEFAULT_ACTION\n", (LPCSTR)CW2A( pwzUrl ), ParseAction );
	return INET_E_DEFAULT_ACTION;
}

STDMETHODIMP CMirandaProtocol::CombineUrl(LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR /*pwzResult*/, DWORD /*cchResult*/, DWORD* /*pcchResult*/, DWORD /*dwReserved*/)
{
	pwzBaseUrl;
	pwzRelativeUrl;
	dwCombineFlags;
	ATLTRACE( "IInternetProtocolInfo : CombineUrl( \"%s\", \"%s\", 0x%08x ) -> INET_E_DEFAULT_ACTION\n", (LPCSTR)CW2A( pwzBaseUrl ), (LPCSTR)CW2A( pwzRelativeUrl ), dwCombineFlags );
	return INET_E_DEFAULT_ACTION;
}

STDMETHODIMP CMirandaProtocol::CompareUrl(LPCWSTR pwzUrl1, LPCWSTR pwzUrl2, DWORD dwCompareFlags)
{
	pwzUrl1;
	pwzUrl2;
	dwCompareFlags;
	ATLTRACE( "IInternetProtocolInfo : CompareUrl( \"%s\", \"%s\", 0x%08x ) -> INET_E_DEFAULT_ACTION\n", (LPCSTR)CW2A( pwzUrl1 ), (LPCSTR)CW2A( pwzUrl2 ), dwCompareFlags );
	return INET_E_DEFAULT_ACTION;
}

STDMETHODIMP CMirandaProtocol::QueryInfo(LPCWSTR pwzUrl, QUERYOPTION OueryOption, DWORD /*dwQueryFlags*/, LPVOID pBuffer, DWORD cbBuffer, DWORD *pcbBuf, DWORD /*dwReserved*/)
{
	pwzUrl;

	switch ( OueryOption )
	{
	case QUERY_USES_NETWORK:
		*pcbBuf = sizeof( DWORD );
		if ( cbBuffer < *pcbBuf || pBuffer == NULL )
		{
			ATLTRACE( "IInternetProtocolInfo : QueryInfo( \"%s\", QUERY_USES_NETWORK ) -> S_FALSE\n", (LPCSTR)CW2A( pwzUrl ) );
			return S_FALSE;
		}
		*(DWORD*)pBuffer = FALSE;
		ATLTRACE( "IInternetProtocolInfo : QueryInfo( \"%s\", QUERY_USES_NETWORK ) -> S_OK (false)\n", (LPCSTR)CW2A( pwzUrl ) );
		return S_OK;

	case QUERY_IS_SECURE:
		*pcbBuf = sizeof( DWORD );
		if ( cbBuffer < *pcbBuf || pBuffer == NULL )
		{
			ATLTRACE( "IInternetProtocolInfo : QueryInfo( \"%s\", QUERY_IS_SECURE ) -> S_FALSE\n", (LPCSTR)CW2A( pwzUrl ) );
			return S_FALSE;
		}
		*(DWORD*)pBuffer = FALSE;
		ATLTRACE( "IInternetProtocolInfo : QueryInfo( \"%s\", QUERY_IS_SECURE ) -> S_OK (insecure)\n", (LPCSTR)CW2A( pwzUrl ) );
		return S_OK;

	case QUERY_IS_SAFE:
		*pcbBuf = sizeof( DWORD );
		if ( cbBuffer < *pcbBuf || pBuffer == NULL )
		{
			ATLTRACE( "IInternetProtocolInfo : QueryInfo( \"%s\", QUERY_IS_SAFE ) -> S_FALSE\n", (LPCSTR)CW2A( pwzUrl ) );
			return S_FALSE;
		}
		*(DWORD*)pBuffer = TRUE;
		ATLTRACE( "IInternetProtocolInfo : QueryInfo( \"%s\", QUERY_IS_SAFE ) -> S_OK (safe)\n", (LPCSTR)CW2A( pwzUrl ) );
		return S_OK;
	}

	ATLTRACE( "IInternetProtocolInfo : QueryInfo( \"%s\", %d ) -> INET_E_DEFAULT_ACTION\n", (LPCSTR)CW2A( pwzUrl ), OueryOption );
	return INET_E_DEFAULT_ACTION;
}

//// IShellFolder
//
//STDMETHODIMP CMirandaProtocol::ParseDisplayName( 
//    /* [unique][in] */ __RPC__in_opt HWND hwnd,
//    /* [unique][in] */ __RPC__in_opt IBindCtx *pbc,
//    /* [string][in] */ __RPC__in_string LPWSTR pszDisplayName,
//    /* [annotation][unique][out][in] */ __reserved  ULONG *pchEaten,
//    /* [out] */ __RPC__deref_out_opt PIDLIST_RELATIVE *ppidl,
//    /* [unique][out][in] */ __RPC__inout_opt ULONG *pdwAttributes)
//{
//	ATLTRACE( "IShellFolder : ParseDisplayName( \"%s\" )\n", (LPCSTR)CW2A( pszDisplayName ) );
//	
//	if ( pchEaten )
//		*pchEaten = (ULONG)wcslen( pszDisplayName );
//
//	CComHeapPtr< ITEMIDLIST_RELATIVE > spidl;
//	spidl.Allocate( 1 );
//	spidl->mkid.cb = sizeof( SHITEMID );
//	spidl->mkid.abID[ 0 ] = 0;
//	if ( ppidl )
//		*ppidl = spidl.Detach();
//
//	if ( pdwAttributes )
//		*pdwAttributes = 0;
//
//	return S_OK;
//}
//
//STDMETHODIMP CMirandaProtocol::EnumObjects( 
//    /* [unique][in] */ __RPC__in_opt HWND hwnd,
//    /* [in] */ SHCONTF grfFlags,
//    /* [out] */ __RPC__deref_out_opt IEnumIDList **ppenumIDList)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : EnumObjects") );
//}
//
//STDMETHODIMP CMirandaProtocol::BindToObject( 
//    /* [in] */ __RPC__in PCUIDLIST_RELATIVE pidl,
//    /* [unique][in] */ __RPC__in_opt IBindCtx *pbc,
//    /* [in] */ __RPC__in REFIID riid,
//    /* [iid_is][out] */ __RPC__deref_out_opt void **ppv)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : BindToObject") );
//}
//
//STDMETHODIMP CMirandaProtocol::BindToStorage( 
//    /* [in] */ __RPC__in PCUIDLIST_RELATIVE pidl,
//    /* [unique][in] */ __RPC__in_opt IBindCtx *pbc,
//    /* [in] */ __RPC__in REFIID riid,
//    /* [iid_is][out] */ __RPC__deref_out_opt void **ppv)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : BindToStorage") );
//}
//
//STDMETHODIMP CMirandaProtocol::CompareIDs( 
//    /* [in] */ LPARAM lParam,
//    /* [in] */ __RPC__in PCUIDLIST_RELATIVE pidl1,
//    /* [in] */ __RPC__in PCUIDLIST_RELATIVE pidl2)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : CompareIDs") );
//}
//
//STDMETHODIMP CMirandaProtocol::CreateViewObject( 
//    /* [unique][in] */ __RPC__in_opt HWND hwndOwner,
//    /* [in] */ __RPC__in REFIID riid,
//    /* [iid_is][out] */ __RPC__deref_out_opt void **ppv)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : CreateViewObject") );
//}
//
//STDMETHODIMP CMirandaProtocol::GetAttributesOf( 
//    /* [in] */ UINT cidl,
//    /* [unique][size_is][in] */ __RPC__in_ecount_full_opt(cidl) PCUITEMID_CHILD_ARRAY apidl,
//    /* [out][in] */ __RPC__inout SFGAOF *rgfInOut)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : GetAttributesOf") );
//}
//
//STDMETHODIMP CMirandaProtocol::GetUIObjectOf( 
//    /* [unique][in] */ __RPC__in_opt HWND hwndOwner,
//    /* [in] */ UINT cidl,
//    /* [unique][size_is][in] */ __RPC__in_ecount_full_opt(cidl) PCUITEMID_CHILD_ARRAY apidl,
//    /* [in] */ __RPC__in REFIID riid,
//    /* [annotation][unique][out][in] */ __reserved  UINT *rgfReserved,
//    /* [iid_is][out] */ __RPC__deref_out_opt void **ppv)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : GetUIObjectOf") );
//}
//
//STDMETHODIMP CMirandaProtocol::GetDisplayNameOf( 
//    /* [unique][in] */ __RPC__in_opt PCUITEMID_CHILD pidl,
//    /* [in] */ SHGDNF uFlags,
//    /* [out] */ __RPC__out STRRET *pName)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : GetDisplayNameOf") );
//}
//
//STDMETHODIMP CMirandaProtocol::SetNameOf( 
//    /* [annotation][unique][in] */ __in_opt  HWND hwnd,
//    /* [annotation][in] */ __in  PCUITEMID_CHILD pidl,
//    /* [annotation][string][in] */ __in  LPCWSTR pszName,
//    /* [annotation][in] */ __in  SHGDNF uFlags,
//    /* [annotation][out] */ __deref_opt_out  PITEMID_CHILD *ppidlOut)
//{
//	ATLTRACENOTIMPL( _T("IShellFolder : SetNameOf") );
//}
//
//// IPersist
//
//STDMETHODIMP CMirandaProtocol::GetClassID( 
//	/* [out] */ __RPC__out CLSID *pClassID)
//{
//	*pClassID = __uuidof( MirandaProtocol );
//
//	ATLTRACE( "0x%08x : CMirandaProtocol : IPersist::GetClassID()\n", GetCurrentThreadId() );
//
//	return S_OK;
//}
//
//// IPersistFolder
//
//STDMETHODIMP CMirandaProtocol::Initialize( 
//	/* [in] */ __RPC__in PCIDLIST_ABSOLUTE /*pidl*/)
//{
//	ATLTRACE( "0x%08x : CMirandaProtocol : IPersistFolder::Initialize()\n", GetCurrentThreadId() );
//
//	return S_OK;
//}
//	
//// IDelegateFolder
//	
//STDMETHODIMP CMirandaProtocol::SetItemAlloc( 
//	/* [annotation][in] */ __in  IMalloc* /*pmalloc*/)
//{
//	ATLTRACE( "0x%08x : CMirandaProtocol : IDelegateFolder::SetItemAlloc()\n", GetCurrentThreadId() );
//
//	return S_OK;
//}
